#include <winhack.h>
//#include <elasys_server.h>
#include <elapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysconfig.h>
#include <sharemem.h>

#include "camplus.h"

#define PHOTO_FRAMERATE 5 // Snap a frame every PHOTO_FRAMERATE frames


void PhotoSnapper::InitPhotoSnapperFormat()
{
    // Though the setting of these values are not implemented in virtual marchine,
    // We can also set and get them so that the application can run without error.
    // Now set the default values.
    m_SnapperFormat.PhotoResolution         = CAMERA_PR_LOW;
    m_SnapperFormat.m_iFrameRate            = 0; // 0 means using the default value
    m_SnapperFormat.m_iZoom                 = 1;
    m_SnapperFormat.m_iBrightness           = 50;
    m_SnapperFormat.m_iContrast             = 50;
    m_SnapperFormat.m_iSharpness            = 0;
    m_SnapperFormat.m_iWhiteBalance         = CAMERA_WB_AUTO;
    m_SnapperFormat.m_iColorMode            = CAMERA_CM_COLOR;
    m_SnapperFormat.m_iSceneMode            = CAMERA_SM_AUTO;
    m_SnapperFormat.m_iDirection            = CAMERA_DR_HORIZONTAL;
    m_SnapperFormat.m_iFlash                = CAMERA_FL_CLOSED;
    m_SnapperFormat.m_iISO                  = 200;
    m_SnapperFormat.m_iExposureCompensation = 0;
    m_SnapperFormat.m_iContinuousSnaps      = 1;
}

void PhotoSnapper::ResolutionToSize(int Resolution,
                      unsigned long *pWidth,
                      unsigned long *pHeight)
{
    unsigned long uLCDWidth, uLCDHeigh;

    if (NOERROR != ConfigGetItemInt(L"LCDWidth",
       (int *)&(uLCDWidth))) {
        uLCDWidth = 240;
    }

    if (NOERROR != ConfigGetItemInt(L"LCDHeight",
        (int *)&(uLCDHeigh))) {
        uLCDHeigh = 320;
    }

    switch (Resolution) {
        case CAMERA_PR_LOW:
            *pWidth = 480;
            *pHeight = 640;
            break;
        case CAMERA_PR_MEDIUM:
//#if defined _pc_fg700

            *pWidth = 600;
            *pHeight = 800;
//#else
//            *pWidth = 480;
//            *pHeight = 640;
//#endif
            break;
        case CAMERA_PR_HIGH:
//#if defined  _pc_fg700
            *pWidth = 1200;
            *pHeight = 1600;
//#else
//            *pWidth = 720;
//            *pHeight = 960;
//#endif
            break;
        default:
            *pWidth = uLCDWidth;
            *pHeight = uLCDHeigh;
            break;
    }
}

ECode PhotoSnapper::SetSnapSize()
{
    BOOL ret;
    ECode ec = NOERROR;

    ResolutionToSize(m_SnapperFormat.PhotoResolution,
                    &m_ulSnapFrameWidth,
                    &m_ulSnapFrameHeight);

    ret = m_pShower->SetShowFormat(m_ulSnapFrameWidth,
                                   m_ulSnapFrameHeight);
    if(!ret){
        printf("Error: PhotoSnapper SetSnapSize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = E_INVALID_OPERATION;
        goto EXIT;
    }

    ret = m_pConverter->SetDesFormat(m_ulSnapFrameWidth,
                                     m_ulSnapFrameHeight);
    if(!ret){
        printf("Error: PhotoSnapper SetSnapSize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = E_INVALID_OPERATION;
        goto EXIT;
    }

    ret = SetSnapBuffer();
    if(!ret){
        printf("Error: PhotoSnapper SetSnapSize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = E_OUT_OF_MEMORY;
        goto EXIT;
    }

EXIT:
    return ec;
}

BOOL PhotoSnapper::SetSnapBuffer()
{
    m_ulSnapBufFrameLen = m_ulSnapFrameWidth
                         * m_ulSnapFrameHeight
                         * YCBCR_422_BYTES;
    if(NULL != m_pSnapFramedBuf){
        FreeAllocShareMem((Address)m_pSnapFramedBuf);
        m_pSnapFramedBuf = NULL;
    }
    ECode ec = AllocShareMem(m_ulSnapBufFrameLen, MemoryProtection_RW,
                    (Address *)&m_pSnapFramedBuf, NULL);
    if (FAILED(ec)){
        printf("Error: PhotoSnapper SetSnapBuffer failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return FALSE;
    }
//    m_pSnapFramedBuf = (BYTE *)realloc(m_pSnapFramedBuf,
//        (m_ulSnapBufFrameLen * m_SnapperFormat.m_iContinuousSnaps));
//    if (NULL == m_pSnapFramedBuf){
//        printf("Error: PhotoSnapper SetSnapBuffer failed!
//        file:%s. line:%d. \n",
//        __FILE__, __LINE__);
//        return FALSE;
//    }

    return TRUE;
}

BOOL PhotoSnapper::Initialize(FrameCapture *pCap,
                               FrameDisplay *pShower,
                               RGB2YCbCrConverter *pConverter)
{
    assert(pCap);
    assert(pShower);
    assert(pConverter);

    InitPhotoSnapperFormat();

    m_uSnapCount      = 0;
    m_uCurrentSnapIdx = 0;

    m_pSnapFramedBuf  = NULL;

    m_pCapture        = pCap;

    m_pShower         = pShower;

    m_pConverter      = pConverter;

    m_hSnapLock = CreateMutexW(NULL, FALSE, NULL);
    if(NULL == m_hSnapLock){
        printf("Error: camera PhotoSnapper Initialize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return FALSE;
    }

    m_hSnapEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if(NULL == m_hSnapEvent){
        printf("Error: camera PhotoSnapper Initialize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        CloseHandle(m_hSnapLock);
        m_hSnapLock = NULL;
        return FALSE;
    }

    //Init the zoom with recorder format
    m_pConverter->SetDigitalZoom(m_SnapperFormat.m_iZoom);

    if(FAILED(SetSnapSize())){
        printf("Error: camera PhotoSnapper Initialize failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        CloseHandle(m_hSnapLock);
        m_hSnapLock = NULL;
        CloseHandle(m_hSnapEvent);
        m_hSnapEvent = NULL;
        return FALSE;
    }

    return TRUE;
}

void  PhotoSnapper::Destroy()
{
    //Incase the capture still open
    m_pCapture->StopCapture();

    m_pShower->ResetShowFormat();

    if(NULL != m_pSnapFramedBuf){
        FreeAllocShareMem((Address)m_pSnapFramedBuf);
        m_pSnapFramedBuf = NULL;
    }

    if(NULL != m_hSnapEvent){
        CloseHandle(m_hSnapEvent);
        m_hSnapLock = NULL;
    }

    if(NULL != m_hSnapLock){
        CloseHandle(m_hSnapLock);
        m_hSnapLock = NULL;
    }
}


ECode PhotoSnapper::GetPhotoSnapperFormat(
                        unsigned int nID, int *pwParam, int *plParam)
{
    ECode ec = NOERROR;

    switch (nID) {
    case CAMERA_FORMAT_PHOTO_RESOLUTION:
        *pwParam = m_SnapperFormat.PhotoResolution;
        break;
    //BUGBUG:The AppResolution width > Height, different from internal
    case CAMERA_FORMAT_PHOTO_GETSIZE:
        *pwParam = (int)m_ulSnapFrameHeight;
        *plParam =  (int)m_ulSnapFrameWidth;
        break;
    case CAMERA_FORMAT_ZOOM:
        *pwParam = m_SnapperFormat.m_iZoom;
        *plParam = 1;
        break;
    case CAMERA_FORMAT_BRIGHTNESS:
        *pwParam = m_SnapperFormat.m_iBrightness;
        break;
    case CAMERA_FORMAT_CONTRAST:
        *pwParam = m_SnapperFormat.m_iContrast;
        break;
    case CAMERA_FORMAT_SHARPNESS:
        *pwParam = m_SnapperFormat.m_iSharpness;
        break;
    case CAMERA_FORMAT_WHITEBALANCE:
        *pwParam = m_SnapperFormat.m_iWhiteBalance;
        break;
    case CAMERA_FORMAT_COLORMODE:
        *pwParam = m_SnapperFormat.m_iColorMode;
        break;
    case CAMERA_FORMAT_SCENEMODE:
        *pwParam = m_SnapperFormat.m_iSceneMode;
        break;
    case CAMERA_FORMAT_FLASH:
        *pwParam = m_SnapperFormat.m_iFlash;
        break;
    case CAMERA_FORMAT_EXPOSURE_COMPENSATION:
        *pwParam = m_SnapperFormat.m_iExposureCompensation;
        break;
    case CAMERA_FORMAT_CONTINUOUS_SNAP:
        *pwParam = m_SnapperFormat.m_iContinuousSnaps;
        break;
    case CAMERA_FORMAT_PHOTO_GETFRAMESIZE:
        *pwParam = m_ulSnapFrameHeight *
            m_ulSnapFrameWidth * YCBCR_422_BYTES;
        break;
    default:
        ec = E_NOT_IMPLEMENTED;
        break;
    }

    return ec;
}

ECode  PhotoSnapper::SetPhotoSnapperFormat(
                        unsigned int nID, int wParam, int lParam)
{
    ECode ec = NOERROR;

    switch (nID) {
    case CAMERA_FORMAT_PHOTO_RESOLUTION:
        if (wParam > 2) wParam = 2;
        if (wParam < 0) wParam = 0;
        if(wParam != m_SnapperFormat.PhotoResolution){
            WaitForSingleObject(m_hSnapLock, (DWORD)INFINITE);
            m_SnapperFormat.PhotoResolution = wParam;
            ec = SetSnapSize();
            ReleaseMutex(m_hSnapLock);
        }
        break;
    case CAMERA_FORMAT_ZOOM:
        {
            int zoom = wParam / lParam;
            if (zoom < 1 || zoom > DIGITALZOOM_MAX){
                zoom = 1;
            }
            m_SnapperFormat.m_iZoom = zoom;
            m_pConverter->SetDigitalZoom(zoom);
            m_pShower->SetDigitalZoom(zoom);
        }
        break;
    case CAMERA_FORMAT_BRIGHTNESS:
        if (wParam > 255) wParam = 255;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iBrightness = wParam;
        break;
    case CAMERA_FORMAT_CONTRAST:
        if (wParam > 99) wParam = 99;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iContrast = wParam;
        break;
    case CAMERA_FORMAT_SHARPNESS:
        if (wParam > 99) wParam = 99;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iSharpness = wParam;
        break;
    case CAMERA_FORMAT_WHITEBALANCE:
        if (wParam > 99) wParam = 99;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iWhiteBalance = wParam;
        break;
    case CAMERA_FORMAT_COLORMODE:
        if (wParam > 6) wParam = 6;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iColorMode = wParam;
        break;
    case CAMERA_FORMAT_SCENEMODE:
        if (wParam > 5) wParam = 5;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iSceneMode = wParam;
        break;
    case CAMERA_FORMAT_EXPOSURE_COMPENSATION:
        m_SnapperFormat.m_iExposureCompensation = wParam;
        break;
    case CAMERA_FORMAT_FLASH:
        if (wParam > 4) wParam = 4;
        if (wParam < 0) wParam = 0;
        m_SnapperFormat.m_iFlash = wParam;
        break;
    case CAMERA_FORMAT_CONTINUOUS_SNAP:
        if (wParam <= 0) wParam = 1;
        WaitForSingleObject(m_hSnapLock, (DWORD)INFINITE);
        m_SnapperFormat.m_iContinuousSnaps = wParam;
        SetSnapBuffer();
        ReleaseMutex(m_hSnapLock);
        break;
    default:
        ec = E_NOT_IMPLEMENTED;
        break;
    }

    return ec;
}

void PhotoSnapper::GetSnapDest(YCbCrBufferInfo *pYCbCrInfo)
{
    unsigned long YChannelSize;
    int iCurrentAd;

    YChannelSize    = m_ulSnapBufFrameLen / 2;
    iCurrentAd      = m_uCurrentSnapIdx * m_ulSnapBufFrameLen;
    pYCbCrInfo->pY  = (BYTE *)(&m_pSnapFramedBuf[iCurrentAd]);
    pYCbCrInfo->pCb = pYCbCrInfo->pY + YChannelSize;
    pYCbCrInfo->pCr = pYCbCrInfo->pCb+ YChannelSize / 2;
}



void PhotoSnapper::DoSnap(void *pFrameBuffer)
{
    YCbCrBufferInfo YCbCrInfo;

    if (m_uSnapCount > 0) {
        m_uSnapCount--;

        if ((m_uSnapCount % PHOTO_FRAMERATE) == 0) {

            m_pShower->ShowFrame(pFrameBuffer);

            GetSnapDest(&YCbCrInfo);

            m_pConverter->RGBTo422Rotary90((BYTE *)pFrameBuffer, YCbCrInfo);

            m_uCurrentSnapIdx++;
        }

        if (m_uSnapCount == 0) {
            ::SetEvent(m_hSnapEvent);
        }
    }
}

void PhotoSnapperCallBack(void *pFrameBuffer){
    assert(g_CameraDriver.m_PhotoSnapper);
    g_CameraDriver.m_PhotoSnapper->DoSnap(pFrameBuffer);
}

#define SANP_WAIT_TIME 5000

ECode PhotoSnapper::SnapPhotos(Camera_Photo *pPhoto)
{
    BOOL ret;
    ECode ec = NOERROR;
    unsigned long uSnapFramedBufId = 0;

    m_pCapture->StopCapture();

    WaitForSingleObject(m_hSnapLock, (DWORD)INFINITE);

    ret = m_pShower->SetShowFormat(m_ulSnapFrameWidth,
                                  m_ulSnapFrameHeight);
    if(!ret){
        printf("Error: camera PhotoSnapper SnapPhotos failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = E_INVALID_OPERATION;
        goto EXIT;
    }

    m_uSnapCount = (m_SnapperFormat.m_iContinuousSnaps - 1)
                   * PHOTO_FRAMERATE + 1;

    m_pCapture->BeginCapture(PhotoSnapperCallBack);

    WaitForSingleObject(m_hSnapEvent, SANP_WAIT_TIME);

    m_uCurrentSnapIdx = 0;

    m_pCapture->StopCapture();

    ret = m_pShower->ResetShowFormat();
    assert(ret);
    if(!ret){
        ec = E_INVALID_OPERATION;
    }

    pPhoto->Number = m_SnapperFormat.m_iContinuousSnaps;
    pPhoto->SingleBufferSize = m_ulSnapBufFrameLen;
    ConvertAllocAddrToId((Address)m_pSnapFramedBuf, 0, &uSnapFramedBufId);
    pPhoto->pBuffer = (BYTE *)uSnapFramedBufId;

EXIT:
    ReleaseMutex(m_hSnapLock);

    return ec;

}

//todo: add lock
